﻿// Originally contributed by Chinajade.
//
// LICENSE:
// This work is licensed under the
//     Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// also known as CC-BY-NC-SA.  To view a copy of this license, visit
//      http://creativecommons.org/licenses/by-nc-sa/3.0/
// or send a letter to
//      Creative Commons // 171 Second Street, Suite 300 // San Francisco, California, 94105, USA.

#region Usings
using System;
using System.Collections.Generic;
using System.Linq;

using Styx.Common.Helpers;
using Styx.CommonBot;
using Styx.Helpers;
using Styx.TreeSharp;
using Styx.WoWInternals.WoWObjects;

using Action = Styx.TreeSharp.Action;
#endregion


namespace Honorbuddy.QuestBehaviorCore
{
	public partial class UtilityBehaviorPS
	{
		/// <summary>
		/// <para>This behavior waits MAXWAITTIMEINMILLISECONDSDELEGATE for item ITEMIDDELEGATE to arrive in our bags.
		/// If the item doesn't arrived within the specified time, the behavior is terminated.
		/// </para>
		/// </summary>
		/// <remarks>11May2013-08:25UTC chinajade</remarks>
		public class WaitForInventoryItem : PrioritySelector
			
		{
			public WaitForInventoryItem(ProvideIntDelegate itemIdDelegate,
										Action<object> actionOnExpiredTimerDelegate,
										ProvideIntDelegate maxWaitTimeInMillisecondsDelegate = null)
			{
				Contract.Requires(itemIdDelegate != null, context => "itemIdDelegate != null");
				Contract.Requires(actionOnExpiredTimerDelegate != null, context => "actionOnExpiredTimerDelegate != null");

				ItemIdDelegate = itemIdDelegate;
				ActionOnExpiredTimerDelegate = actionOnExpiredTimerDelegate;
				MaxWaitTimeInMillisecondsDelegate = maxWaitTimeInMillisecondsDelegate ?? (context => 5000);

				Children = CreateChildren();
			}


			// BT contruction-time properties...
			private Action<object> ActionOnExpiredTimerDelegate { get; set; }
			private ProvideIntDelegate ItemIdDelegate { get; set; }
			private ProvideIntDelegate MaxWaitTimeInMillisecondsDelegate { get; set; }

			// BT visit-time properties...
			private WoWItem CachedWoWItem { get; set; }
			private WaitTimer WatchdogTimer_WaitForItemArrival { get; set; }


			private List<Composite> CreateChildren()
			{
				// NB: Two particular scenarios that this behavior must always perform correctly in...
				// 1) Item not in our bags, the timer is started, we get in combat, when we get out of combat the timer is expired.
				//      The behavior must be certain to check (again) that the item is not in our bag, before terminating the
				//      behavior due to timer expiration.
				// 2) On the last use of the item, it disappears from our bag.
				//      We must address the case where the WoWclient may destroy the item before marking the quest as complete.
				//      So, when the item disappears, we must start the timer again, and allow the quest to complete
				//      before we terminate the behavior due to timer expiration.
				return new List<Composite>()
				{
					new Action(context =>
					{
						CachedWoWItem = Me.CarriedItems.FirstOrDefault(i => (i.Entry == ItemIdDelegate(context)));

						if (Query.IsViable(CachedWoWItem))
						{
							// Squelch the timer, so it will be available if the item disappears from our bags...
							WatchdogTimer_WaitForItemArrival = null;
							return RunStatus.Failure;
						}

						// If timer is not spinning, create & start it...
						if (WatchdogTimer_WaitForItemArrival == null)
						{
							WatchdogTimer_WaitForItemArrival = new WaitTimer(TimeSpan.FromMilliseconds(MaxWaitTimeInMillisecondsDelegate(context)));
							WatchdogTimer_WaitForItemArrival.Reset();                                    
						}

						// If timer completes, time to call it quits...
						if (WatchdogTimer_WaitForItemArrival.IsFinished)
						{
							QBCLog.ProfileError(
								"{0} has not arrived in our bag within {1}.",
								Utility.GetItemNameFromId(ItemIdDelegate(context)),
								Utility.PrettyTime(WatchdogTimer_WaitForItemArrival.WaitTime));
							ActionOnExpiredTimerDelegate(context);                                     
						}

						else
						{
							TreeRoot.StatusText = string.Format("Waiting {0} for {1} to arrive in our bags.",
								Utility.PrettyTime(WatchdogTimer_WaitForItemArrival.WaitTime),
								Utility.GetItemNameFromId(ItemIdDelegate(context)));
						}

						return RunStatus.Success;
					})
				};
			}
		}
	}
}